package com.example.oj_springboot.service.util;

import com.example.oj_springboot.model.CompileResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 编译+运行 并获得执行结果
 */
public class Task {
    private String WORK_DIR;// 表示文件所在的目录
    private String CLASS = "Solution";// 约定代码的类名
    private String CODE;// 约定要编译执行的代码的文件名
    private String STDOUT;// 约定存放运行时标准输出文件名
    private String STDERR;// 约定存放运行时标准错误文件名
    private String COMPILE_ERROR;// 约定存放编译错误信息的文件名

    public Task() {
        //生成唯一的id做为目录
        WORK_DIR = "./tmp/" + UUID.randomUUID().toString() + "/";
        //生成其他文件名
        CODE = WORK_DIR + CLASS + ".java";
        STDOUT = WORK_DIR + "stdout.txt";
        STDERR = WORK_DIR + "stderr.txt";
        COMPILE_ERROR = WORK_DIR + "compile_error.txt";
    }

    /**
     * 编译+运行
     * @param code
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public CompileResponse compileAndRun(String code) throws IOException, InterruptedException {
        CompileResponse compileResponse = new CompileResponse();

        //准备用来存放临时文件的目录
        File file = new File(WORK_DIR);
        if (!file.exists()) {
            file.mkdirs();
        }

        //进行安全性判定
        if (!checkCodeSafe(code)){
            compileResponse.setError(3);
            compileResponse.setReason("您提交的代码不安全");
        }

        //1.把code写入到Solution.java文件中
        FileUtil.writeFile(CODE, code);

        //2.通过javac进行编译，-d表示生成的.class文件放置的位置
        String compileCmd = String.format("javac -encoding utf-8 %s -d %s", CODE, WORK_DIR);
        System.out.println("编译命令: " + compileCmd);
        //创建子进程进行编译
        CommandUtil.run(compileCmd, null, COMPILE_ERROR);
        //如果编译出的文件COMPILE_ERROR不为空，则表示编译出错
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")) {
            compileResponse.setError(1);
            compileResponse.setReason(compileError);
            return compileResponse;
        }

        //3.通过java进行执行，-classpath通过这个选项来运行，表示生成的.class文件放置的位置
        String runCmd = String.format("java -classpath %s %s", WORK_DIR, CLASS);
        System.out.println("执行命令: " + runCmd);
        //创建子进程进行编译
        CommandUtil.run(runCmd, STDOUT, STDERR);
        //如果执行出的文件STDERR不为空, 则表示运行出错
        String runError = FileUtil.readFile(STDERR);
        if (!runError.equals("")) {
            compileResponse.setError(2);
            compileResponse.setReason(runError);
            return compileResponse;
        }

        //4.如果编译+运行成功，构造对应的compileResponse并返回
        compileResponse.setError(0);
        String runStdout = FileUtil.readFile(STDOUT);
        compileResponse.setReason(runStdout);
        return compileResponse;
    }

    /**
     * 进行安全性判定
     * @param code
     * @return
     */
    private boolean checkCodeSafe(String code) {
        List<String> blackList = new ArrayList<>();
        //防止提交的代码运行恶意程序
        blackList.add("Runtime");
        blackList.add("exec");
        //禁止提交的代码读写文件
        blackList.add("java.io");
        //禁止提交的代码访问网络
        blackList.add("java.net");
        for (String target:blackList) {
            int pos = code.indexOf(target);
            if (pos>=0){
                return false;
            }
        }
        return true;
    }

//    public static void main(String[] args) throws IOException, InterruptedException {
//        Task task = new Task();
//        String code = new String("public class Solution {\n" +
//                "    public static void main(String[] args) {\n" +
//                "        System.out.println(\"stdout\");\n" +
//                "        String s = null;\n" +
//                "        System.out.println(s.length());\n" +
//                "    }\n" +
//                "}\n");
//        CompileResponse compileResponse = task.compileAndRun(code);
//        System.out.println(compileResponse);
//    }
}